package config

import (
	"context"
	"crypto/tls"
	"fmt"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
	"github.com/rs/zerolog"
	"net/http"
	"ruoyi-go-echo-ddd/internal/infrastructure/persistence/prop"
	"ruoyi-go-echo-ddd/internal/infrastructure/utils"
	"sync"
	"time"
)

// MinioCfg nacos中的minion结构
type MinioCfg struct {
	Minio prop.MinioConfig `yaml:"minio"`
}

// minio客户端初始化，只执行一次
var minioOnce = sync.OnceValues(func() (*minio.Client, error) {
	logger := zerolog.DefaultContextLogger
	minConfig := &prop.MinioConfig{}

	minioCfg, err := GetConfigFromNacos[MinioCfg](NacosClient, "minio", "DEFAULT_GROUP", "yml")
	if err != nil {
		logger.Error().Err(err).Msg("从nacos获取minio配置错误,使用默认配置")
		minConfig = &MinIoConfig
	} else {
		minConfig = &minioCfg.Minio
	}

	// 配置校验
	if minConfig.Endpoint == "" || minConfig.AccessKeyId == "" {
		return nil, fmt.Errorf("minio的endpoint与access-key-id不能为空")
	}

	// 验证minio客户端
	client, err := newMinioClient(minConfig)
	if err != nil {
		logger.Error().Err(err).Msg("minio客户端初始化错误，准备从默认配置初始化")
		return nil, fmt.Errorf("minio客户端初始化失败: %w", err)
	}

	// 查询minio的桶验证minion连接性
	if err := minionBucket(context.Background(), client); err != nil {
		logger.Error().Err(err).Msgf("minio探活失败，使用默认配置初始化===\n%s", utils.ToJsonFormat(&MinIoConfig))
		newClient, err := newMinioClient(&MinIoConfig)
		if err != nil {
			logger.Error().Err(err).Msg("默认配置初始化minio客户端错误")
			return nil, err
		} else {
			if err := minionBucket(context.Background(), newClient); err != nil {
				return nil, err
			}
			client = newClient
			logger.Info().Msg("minio探活成功，成功连接到客户端")
		}
	}
	return client, nil
})

// InitMinioClient 线程安全的单例初始化
func InitMinioClient() (*minio.Client, error) {
	return minioOnce()
}

func newMinioClient(minConfig *prop.MinioConfig) (*minio.Client, error) {
	// 初始化客户端
	return minio.New(minConfig.Endpoint, &minio.Options{
		Creds: credentials.NewStaticV4(
			minConfig.AccessKeyId,
			minConfig.SecretAccessKey,
			"",
		),
		Secure: minConfig.UseSSL,
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: minConfig.UseSSL,
			},
			MaxIdleConns:    100,
			IdleConnTimeout: 90 * time.Second,
		},
	})
}

// minionBucket 查询桶信息眼验证minio客户端是否正常
func minionBucket(ctx context.Context, client *minio.Client) error {
	ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()
	_, err := client.ListBuckets(ctx)
	return err
}
